/**
 * @(#)Filter.java
 *
 * Copyright: Copyright (c) 2003 Carnegie Mellon University
 *
 * This program is the implementation of the original system in the Assignment 1 of "Architectures
 * of Software Systems" class. For the details of each classes, please refer to the provided javadoc
 * documents. You will also find some useful information such as implementation decisions, coding
 * standards, and naming conventions in the README file.
 *
 * History:
 *
 * Date          Author              Change
 * ----------    ----------------    ---------------------------------------------------------------
 * 07/15/2003    Jung Soo Kim        Initial version.
 */


import java.io.*;


/**
 * This class defines a skeletal filter that reads data from one or more input ports and writes data
 * to one or more output ports. "Ports" are bound to the "roles" of pipes that connect filters. Such
 * binding information is not the direct concern of individual filters. Actually a filter does not 
 * even know the presence of other filters/pipes. (Then who?)
 * <p>
 * This class defines when a filter works and dies. (Note that a <code>Filter</code> object is also
 * a thread.) Specific details about ports and operations should be concretely specified in the 
 * subclass definitions inheriting this class. For that reason, this class is left as an abstract
 * class that cannot be instantiated.
 *
 * @author Jung Soo Kim
 * @version 1.0
 */
abstract public class Filter extends Thread {

    /**
     * Indicates if this filter is currently working on some data.
     */
    protected boolean bBusy;

    /**
     * Constructs a skeletal filter with the given name. A new filter becomes idle (not working)
     * when created.
     *
     * @param sName the string representing this filter's name
     */
    public Filter(String sName) {
        // Assign the given filter name to the thread name.
        super.setName(sName);

        // Initialize the filter status (idle at the begining).
        this.bBusy = false;
    }

    /**
     * Tells if this filter is working on some data.
     *
     * @return <code>true</code> if and only if this filter is working on some data.
     */
    public boolean busy() {
        return this.bBusy;
    }

    /**
     * The thread body of this filter. A filter reads data from input ports when available and
     * writes data to output ports. During this period, the busy flag turns on. Interrupting a
     * filter causes it to terminate.
     */
    public void run() {
        try {
            while (!super.isInterrupted()) {
                if (this.ready()) {
                    // If data is available, do the job. Busy flag turns on while working.
                    this.bBusy = true;
                    this.work();
                    this.bBusy = false;
                } else {
                    // If not, yield the CPU turn.
                    super.yield();
                }
            }
        } catch (Exception e) {
            // Show debug info and terminate.
            System.out.println("Failure running " + super.getName() + " filter.");
            e.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * Tells if data are available on input ports.
     *
     * @return <code>true</code> if and only if this filter can read data from input ports.
     * @throws IOException
     */
    abstract protected boolean ready() throws IOException;

    /**
     * Read available data from input ports and write new data to output ports.
     *
     * @throws IOException
     */
    abstract protected void work() throws IOException;
}
